/*
 * Copyright (c) 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.samples.apps.iosched.util;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;

import com.firebase.client.Firebase;
import com.google.samples.apps.iosched.BuildConfig;

import java.util.zip.CRC32;

import static com.google.samples.apps.iosched.util.LogUtils.LOGI;
import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag;

/**
 * Utility methods for setting up and interacting with a Firebase account associated with a user
 * account. TODO: instead of passing Context, pass SharedPreferences to methods. See b/27809306.
 */
public class FirebaseUtils {

    private static final String TAG = makeLogTag(AccountUtils.class);

    // These names are are prefixes; the account is appended to them.
    public static final String PREFIX_PREF_FIREBASE_UID = "firebase_uid_";
    public static final String PREFIX_PREF_FIREBASE_URL = "firebase_url_";

    // These names are used to build paths to Firebase child nodes.
    public static final String FIREBASE_NODE_USERS = "users";
    public static final String FIREBASE_NODE_DATA = "data";
    public static final String FIREBASE_NODE_GCM_KEY = "gcm_key";
    public static final String FIREBASE_NODE_VIEWED_VIDEOS = "viewed_videos";
    public static final String FIREBASE_NODE_MY_SESSIONS = "my_sessions";
    public static final String FIREBASE_NODE_IN_SCHEDULE = "in_schedule";
    public static final String FIREBASE_NODE_TIMESTAMP = "timestamp";
    public static final String FIREBASE_NODE_FEEDBACK_SUBMITTED_SESSIONS =
            "feedback_submitted_sessions";
    public static final String FIREBASE_NODE_LAST_ACTIVITY_TIMESTAMP = "last_activity_timestamp";

    /**
     * @param context Context used to lookup {@link SharedPreferences}.
     * @return The user id (UID) generated by Firebase.
     */
    @NonNull
    public static String getFirebaseUid(final Context context) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        return sp.getString(
                AccountUtils.makeAccountSpecificPrefKey(context, PREFIX_PREF_FIREBASE_UID), "");
    }

    /**
     * Stores the UID generated by Firebase in {@link SharedPreferences}.
     *
     * @param context Context used to lookup {@link SharedPreferences}.
     * @param uid     The user id (UID) generated by Firebase.
     */
    public static void setFirebaseUid(final Context context, final String accountName,
            final String uid) {
        LOGI(TAG, "Saving Firebase UID for accountName " + accountName);
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        sp.edit().putString(
                AccountUtils.makeAccountSpecificPrefKey(context, PREFIX_PREF_FIREBASE_UID),
                uid).apply();
    }

    /**
     * Retrieves the Firebase url associated with the current account stored in {@link
     * SharedPreferences}.
     *
     * @param context     Context used to lookup {@link SharedPreferences}.
     * @param accountName The account name associated with the chosen user account.
     * @return The Firebase UID associated with the current account.
     */
    public static String getFirebaseUrl(Context context, String accountName) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        return sp.getString(
                AccountUtils.makeAccountSpecificPrefKey(accountName,
                        PREFIX_PREF_FIREBASE_URL), "");
    }

    /**
     * Calculates the Firebase url associated with an account id generated by calling {@link
     * com.google.android.gms.auth.GoogleAuthUtil#getAccountId(Context, String)}.
     *
     * @param context   Context used to lookup {@link SharedPreferences}.
     * @param accountId The account ID associated with the currently chosen account.
     */
    public static void setFirebaseUrl(Context context, String accountId) {
        String[] firebaseUrls = BuildConfig.FIREBASE_URLS;
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        CRC32 crc = new CRC32();
        crc.update(accountId.getBytes());
        int index = (int) (crc.getValue() % firebaseUrls.length);
        LOGI(TAG, "Selected Firebase db # " + index);
        sp.edit().putString(
                AccountUtils.makeAccountSpecificPrefKey(context, PREFIX_PREF_FIREBASE_URL),
                firebaseUrls[index]).apply();
    }

    /**
     * Builds and returns the Firebase reference for storing the user data with the currently chosen
     * account. TODO: a factory method should not be in a utils file. Remove. See b/27809307.
     */
    @NonNull
    public static Firebase getDataUIDRef(Context context, String accountName) {
        return new Firebase(getFirebaseUrl(context, accountName))
                .child(FIREBASE_NODE_DATA)
                .child(FirebaseUtils.getFirebaseUid(context));
    }

    /**
     * Returns the child path (relative to Firebase root) where all user data except the user's last
     * activity timestamp is stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    @NonNull
    public static String getDataChildPath(String uid) {
        return FIREBASE_NODE_DATA + "/" + uid + "/";
    }

    /**
     * Returns the child path (relative to Firebase root) where the user's last activity timestamp
     * is stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    @NonNull
    public static String getUsersChildPath(String uid) {
        return FIREBASE_NODE_USERS + "/" + uid + "/";
    }

    /**
     * Returns the child path (relative to Firebase root) where the gcm key is stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    public static String getGcmKeyChildPath(String uid) {
        return getDataChildPath(uid) + FIREBASE_NODE_GCM_KEY + "/";
    }

    /**
     * Returns the child path (relative to Firebase root) where the user's last activity timestamp
     * is stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    public static String getLastActivityTimestampChildPath(String uid) {
        return getUsersChildPath(uid) + FIREBASE_NODE_LAST_ACTIVITY_TIMESTAMP + "/";
    }

    /**
     * Returns the Firebase child path (relative to Firebase root) where a user's viewed videos are
     * stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    public static String getViewedVideosChildPath(String uid) {
        return getDataChildPath(uid) + FIREBASE_NODE_VIEWED_VIDEOS + "/";
    }

    /**
     * Returns the Firebase child path (relative to Firebase root) where data for a specific viewed
     * video is stored.
     *
     * @param uid     The Firebase user ID associated with the chosen account.
     * @param videoId The ID of the video that the user watched.
     */
    public static String getViewedVideoChildPath(String uid, String videoId) {
        return getViewedVideosChildPath(uid) + videoId + "/";
    }

    /**
     * Returns the Firebase child path (relative to Firebase root) where a user's starred sessions
     * are stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    public static String getStarredSessionsChildPath(String uid) {
        return getDataChildPath(uid) + FIREBASE_NODE_MY_SESSIONS + "/";
    }

    /**
     * Returns the Firebase child path (relative to the user data ref) where data for a specific
     * starred session is stored.
     *
     * @param uid       The Firebase user ID associated with the chosen account.
     * @param sessionId The ID of the session that was starred.
     */
    public static String getStarredSessionChildPath(String uid, String sessionId) {
        return getStarredSessionsChildPath(uid) + sessionId + "/";
    }

    /**
     * Returns the Firebase child path (relative to the user data ref) where the in-schedule status
     * for a specific starred session is stored.
     *
     * @param uid       The Firebase user ID associated with the chosen account.
     * @param sessionId The ID of the session that was starred.
     */
    public static String getStarredSessionInScheduleChildPath(String uid, String sessionId) {
        return getStarredSessionChildPath(uid, sessionId) + FIREBASE_NODE_IN_SCHEDULE + "/";
    }

    /**
     * Returns the Firebase child path (relative to the user data ref) where the timestamp for a
     * specific starred session is stored.
     *
     * @param uid       The Firebase user ID associated with the chosen account.
     * @param sessionId The ID of the session that was starred.
     */
    public static String getStarredSessionTimestampChildPath(String uid, String sessionId) {
        return getStarredSessionChildPath(uid, sessionId) + FIREBASE_NODE_TIMESTAMP + "/";
    }

    /**
     * Returns the Firebase child path (relative to the user data ref) where a user's feedback
     * submitted sessions are stored.
     *
     * @param uid The Firebase user ID associated with the chosen account.
     */
    public static String getFeedbackSubmittedSessionsChildPath(String uid) {
        return getDataChildPath(uid) + FIREBASE_NODE_FEEDBACK_SUBMITTED_SESSIONS + "/";
    }

    /**
     * Returns the Firebase child path (relative the user data ref) where data where a specific
     * feedback submitted session is stored.
     *
     * @param uid       The Firebase user ID associated with the chosen account.
     * @param sessionId The ID of the session for which feedback was provided.
     */
    public static String getFeedbackSubmittedSessionChildPath(String uid, String sessionId) {
        return getFeedbackSubmittedSessionsChildPath(uid) + sessionId + "/";
    }
}